﻿package net.wangit.jdbc.connection;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Properties;

import net.wangit.framework.exception.ConfigurationException;
import net.wangit.framework.expression.ConstantExpression;
import net.wangit.jdbc.JDBCException;
import net.wangit.jdbc.transaction.JDBCTransaction;
import net.wangit.jdbc.transaction.TransactionImplementor;
import net.wangit.util.Checker;
import net.wangit.util.XMLUtil;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.Element;


public class DriverManagerConnectionProvider implements ConnectionProvider {

	private static final Log log = LogFactory
			.getLog(net.wangit.jdbc.connection.DriverManagerConnectionProvider.class);
	private TransactionImplementor transaction;
	private String url;
	private Properties connectionProps;
	private int isolation;
	private static final HashMap<Integer, String> ISOLATION_LEVELS;

	public DriverManagerConnectionProvider() {
	}

	public void configure(String path, Element connectionNode)
			throws ConnectionException, JDBCException {
		transaction = new JDBCTransaction();
		String driverClass = XMLUtil.getTextString(connectionNode, "class",
				null);
		if (Checker.isEmpty(driverClass)) {
			log.error("driver class is null!");
			throw new ConfigurationException(path, "driver-class");
		}
		try {
			Class.forName(driverClass);
		} catch (ClassNotFoundException cnfe) {
			log.error((new StringBuilder("JDBC driver class not found: "))
					.append(driverClass).toString());
			throw new ConfigurationException(path, "driver-class");
		} catch (Exception e) {
			throw new ConfigurationException(path, "driver-class");
		}
		try {
			isolation = XMLUtil.getTextInt(connectionNode, "isolation", -1);
		} catch (Exception e) {
			throw new ConfigurationException(path, "driver-isolation");
		}
		url = XMLUtil.getTextString(connectionNode, "url", null);
		if (Checker.isEmpty(url)) {
			log.error("url is null!");
			throw new ConfigurationException(path, "driver-url");
		} else {
			url = ConstantExpression.resolve(url).trim();
			connectionProps = getConnectionProperties(connectionNode);
			return;
		}
	}

	public TransactionImplementor getTransaction() {
		return transaction;
	}

	public Connection getConnection() throws SQLException {
		Connection conn = DriverManager.getConnection(url, connectionProps);
		if (isolation > 0)
			conn.setTransactionIsolation(isolation);
		if (!conn.getAutoCommit())
			conn.setAutoCommit(true);
		if (log.isDebugEnabled())
			log.debug((new StringBuilder("created connection to: "))
					.append(url).append(", Isolation Level: ").append(
							isolationLevelToString(conn
									.getTransactionIsolation())).toString());
		return conn;
	}

	public void closeConnection(Connection conn) throws SQLException {
		conn.close();
	}

	private static Properties getConnectionProperties(Element connectionNode) {
		Properties result = new Properties();
		String userName = XMLUtil.getTextString(connectionNode, "username",
				null);
		if (!Checker.isEmpty(userName))
			result.setProperty("user", userName);
		else
			log.warn("no username was specified by property 'driver-username'");
		String password = XMLUtil.getTextString(connectionNode, "password",
				null);
		if (!Checker.isEmpty(password))
			result.setProperty("password", password);
		else
			log.warn("no password was specified by property 'driver-password'");
		List<?> parameters = connectionNode.elements("parameter");
		if (parameters != null) {
			for (int i = 0; i < parameters.size(); i++) {
				Element parameter = (Element) parameters.get(i);
				result.setProperty(parameter.attributeValue("name"), parameter
						.attributeValue("value"));
			}

		}
		return result;
	}

	private String isolationLevelToString(int isolation) {
		return (new StringBuilder(String.valueOf(isolation))).append("(")
				.append(ISOLATION_LEVELS.get(new Integer(isolation)))
				.append(")").toString();
	}

	static {
		ISOLATION_LEVELS = new HashMap<Integer, String>();
		ISOLATION_LEVELS.put(new Integer(0), "NONE");
		ISOLATION_LEVELS.put(new Integer(1), "READ_UNCOMMITTED");
		ISOLATION_LEVELS.put(new Integer(2), "READ_COMMITTED");
		ISOLATION_LEVELS.put(new Integer(4), "REPEATABLE_READ");
		ISOLATION_LEVELS.put(new Integer(8), "SERIALIZABLE");
	}
}
